{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e7682d31-1cf2-4c6d-98b7-75fadfefb6bc",
   "metadata": {},
   "source": [
    "# Quantum Software Development Journey: \n",
    "# From Theory to Application with Classiq - Part 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01dfc0bf-5fdb-4d52-8fb4-168fc930aaff",
   "metadata": {},
   "source": [
    "**Welcome to the Classiq Workshop Series for QClass 2024!**\n",
    "\n",
    "In this series, we will develop the skills needed to participate in quantum software development!\n",
    "\n",
    "- Week 1: Classiq's Basics & High-Level Functional Design\n",
    "- Week 2: Using Git as a Tool for In-Team Collaboration and Open Source Contributions\n",
    "- **Week 3: Introduction to Quantum Machine Learning (QML) & VQE**\n",
    "- Week 4: QNN and and Advanced Applications\n",
    "\n",
    "**[New Classiq's documentation](https://docs.classiq.io/latest/)!**\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "Additional resources you should use are\n",
    "- The IDE of the classiq platform at [platform.classiq.io](platform.classiq.io)\n",
    "- The [community Slack of Classiq](https://short.classiq.io/join-slack) - Classiq's team will answer any question you have over there, including implementation questions\n",
    "- Course's [GitHub repository](https://github.com/Classiq/classiq-library/tree/main/community/QClass_2024)\n",
    "\n",
    "  \n",
    "**Good luck!**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4962a36e-bf41-48a2-9ad0-cbae90f43d98",
   "metadata": {},
   "source": [
    "## VQE"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30587afe-3f19-4c1d-9f88-4a9083e3a158",
   "metadata": {},
   "source": [
    "\n",
    "The Variational Quantum Eigensolver is an algorithm that finds the minimal eigenvalue of a matrix by executing a parametric circuit (also referred to as an ansatz), estimating the expected value of the matrix for the state the circuit creates (from the distribution received by the execution), and using a classical optimizer to select the next set of parameters for the circuit, until reaching convergence (or exceeding a set amount of maximum iterations).\n",
    "\n",
    "The estimation of the expectation value is done on Pauli based matrices, so any matrix we want to perform this operation on, need to be decomposed into a sum of Pauli terms.\n",
    "\n",
    "**In this example, we will create a simple VQE algorithm that estimates the minimal eigenvalue of the  following 2x2 matrix:**\n",
    "\n",
    "`[[1, -1], [-1, 0]] = 1/2*I + 1/2*Z - X`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34dfc66c-b7f1-4ac5-adcb-b0acdbb53fb3",
   "metadata": {},
   "source": [
    "### U Gate"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ab65ac7-73fd-4582-bcf0-0be311e698e5",
   "metadata": {},
   "source": [
    "The single-qubit gate applies phase and rotation with three Euler angles.\n",
    "\n",
    "Matrix representation:\n",
    "\n",
    "$$\n",
    "U(\\gamma,\\phi,\\theta,\\lambda) = e^{i\\gamma}\\begin{pmatrix}\n",
    "\\cos(\\frac{\\theta}{2}) & -e^{i\\lambda}\\sin(\\frac{\\theta}{2}) \\\\\n",
    "e^{i\\phi}\\sin(\\frac{\\theta}{2}) & e^{i(\\phi+\\lambda)}\\cos(\\frac{\\theta}{2}) \\\\\n",
    "\\end{pmatrix}\n",
    "$$\n",
    "\n",
    "Parameters:\n",
    "\n",
    "- `theta`: `CReal`\n",
    "- `phi`: `CReal`\n",
    "- `lam`: `CReal`\n",
    "- `gam`: `CReal`\n",
    "- `target`: `QBit`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee7fdf75-a40b-4eb6-95c7-453b66a4aba7",
   "metadata": {},
   "source": [
    "### VQE Implementation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "df12ed0b-0f92-43c0-8a0a-6d36b12474f2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "from classiq import *\n",
    "\n",
    "HAMILTONIAN = QConstant(\"HAMILTONIAN\", List[PauliTerm], \n",
    "                        [            \n",
    "            PauliTerm([Pauli.I], 0.5),\n",
    "            PauliTerm([Pauli.Z], 0.5),\n",
    "            PauliTerm([Pauli.X], -1)])\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(q: Output[QBit], angles: CArray[CReal, 3]) -> None:\n",
    "    allocate(1, q)\n",
    "    U(angles[0], angles[1], angles[2], 0, q) \n",
    "\n",
    "\n",
    "@cfunc\n",
    "def cmain() -> None:\n",
    "    res = vqe(\n",
    "        HAMILTONIAN,\n",
    "        False,\n",
    "        [],\n",
    "        optimizer=Optimizer.COBYLA, # Constrained Optimization by Linear Approximation\n",
    "        max_iteration=1000,\n",
    "        tolerance=0.001,\n",
    "        step_size=0,\n",
    "        skip_compute_variance=False,\n",
    "        alpha_cvar=1.0,\n",
    "    )\n",
    "    save({\"result\": res})\n",
    "\n",
    "qmod = create_model(main, classical_execution_function=cmain)\n",
    "qprog = synthesize(qmod)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a973762-c597-4519-89e9-a09b0d4b47b9",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary>\n",
    "NOTE\n",
    "\n",
    "</summary>\n",
    "\n",
    "Read more about the supported optimizers:[here](https://docs.classiq.io/latest/reference-manual/built-in-algorithms/ground-state-solving/advanced-usage/solver-customization/)\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb2528fe-9d3e-478e-88f7-c3fbb1761973",
   "metadata": {},
   "source": [
    "Executing from the Classiq Platform:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "5120177b-6737-4922-bae3-d8fef9f6fcf2",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/c44965b8-fb5a-4797-9aeb-741ef2559093?version=0.41.1\n"
     ]
    }
   ],
   "source": [
    "show(qprog)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6e678ca-0813-4a62-a96a-7b37e61065bd",
   "metadata": {},
   "source": [
    "Or directly from the SDK:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "2ed72afa-d3ab-48f1-8b1c-b1de431cff99",
   "metadata": {},
   "outputs": [],
   "source": [
    "res = execute(qprog)\n",
    "# res.open_in_ide()\n",
    "vqe_result = res.result()[0].value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9d7fbad5-ee44-45ef-893e-5a7632994daf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimal energy: -0.60498046875\n",
      "Optimal parameters: {'angles_0': -4.289061524925964, 'angles_1': -6.274092431830335, 'angles_2': -1.1512443092629974}\n",
      "Eigenstate: {'0': (0.5426173548182918+0j), '1': (0.8399800034822258+0j)}\n"
     ]
    }
   ],
   "source": [
    "print(f\"Optimal energy: {vqe_result.energy}\")\n",
    "print(f\"Optimal parameters: {vqe_result.optimal_parameters}\")\n",
    "print(f\"Eigenstate: {vqe_result.eigenstate}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e5662bb-c2e5-44a6-904a-526f9fc266cd",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "### Analytical Solution"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a74428eb-ff56-4f7f-8c3f-7d11136394ce",
   "metadata": {},
   "source": [
    "- VQE is a **numerical** optimization technique used to find the ground state energy of a quantum system.\n",
    "- For simple systems, an **analytical** solution can be achieved by diagonalizing the Hamiltonian matrix.\n",
    "\n",
    "Diagonalization is possible when the Hamiltonian can be represented as a **finite-dimensional matrix** and the matrix is ״well-behaved״ (e.g., Hermitian and non-singular). \n",
    "\n",
    "In summary, for small systems or simple Hamiltonians, the eigenvalues and eigenvectors can be found exactly by diagonalizing the matrix. Let's show an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "aaa82b4f-3ae3-4d61-a337-e0485d3c346a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "H = np.array([[1, -1], [-1, 0]])\n",
    "\n",
    "E , v = np.linalg.eig(H)\n",
    "E_min = min(E)\n",
    "\n",
    "v_min = np.array(v[:,np.argmin(E)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "886fccb5-ce0d-4594-b41f-b5b7a05cef4c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The minimal energy energy is:  -0.6180339887498948\n"
     ]
    }
   ],
   "source": [
    "print(\"The minimal energy energy is: \",E_min)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2a0f981-bfc5-4944-8c01-db1cc97a95f3",
   "metadata": {},
   "source": [
    "**Looks like the VQE estimated the minimum energy well!**\n",
    "\n",
    "Let's have another small validation, this time for the analytical solution of the eigenvalue equation:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acdcebda-6a15-4e83-93b1-013c8d4fcf31",
   "metadata": {},
   "source": [
    "$$ \\hat{H}  V_{min} = E_{min} \\cdot V_{min} \\rightarrow V_{min} = \\frac{\\hat{H}  V_{min}}{E_{min}} $$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39cae87a-5de6-4303-a90a-a3fa31e9f2e7",
   "metadata": {},
   "source": [
    "**Let's validate this relation!**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "6f3107c1-7580-4682-9cdb-ebcb4fc45972",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.52573111, 0.85065081])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v_min"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "fd646a18-cf0c-46af-918a-5e607e4e494b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.52573111, 0.85065081])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(H @ v_min)/E_min "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "db74fb2c-6f55-4c4b-ad84-d23e97a211c7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([False, False])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v_min == (H @ v_min)/E_min"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8351de9e-fcd2-48f8-a2b3-b7480e93009e",
   "metadata": {},
   "source": [
    "**How could it be?**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "f3dc8319-9360-447c-b115-0fdc98e47cc0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The eigenvalue equation is validated!\n"
     ]
    }
   ],
   "source": [
    "if np.allclose((H @ v_min)/E_min, v_min):\n",
    "    print(\"The eigenvalue equation is validated!\")\n",
    "else:\n",
    "    print(\"The eigenvalue equation is not validated!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "054aa87b-8e2b-4abc-b68b-646321532e85",
   "metadata": {},
   "source": [
    "(The \"`array([False, False])`\" was due to rounding errors)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52d61c0b-a0f9-4f7c-88b6-f4333f1d0c2c",
   "metadata": {},
   "source": [
    "## Exercise - Two Qubits VQE"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29e116c2-39f0-4cb4-81da-3828b353bfe6",
   "metadata": {},
   "source": [
    "Now we will practice the implementation of a similar case to the last example, but this time for two qubits, following the Hamiltonian:\n",
    "\n",
    "\n",
    "$H = \\frac{1}{2}I \\otimes I + \\frac{1}{2}Z \\otimes Z - X \\otimes X $\n",
    "\n",
    "Use the last example to implement and execute VQE for this Hamiltonian."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "4b3d4130-98f6-4c92-8266-5bebb4cfdcc8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/518bc08d-8db8-4f62-819c-7153a22b7faf?version=0.41.1\n"
     ]
    }
   ],
   "source": [
    "HAMILTONIAN = QConstant(\"HAMILTONIAN\", List[PauliTerm], [...]) #TODO: Complete Hamiltonian\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(...) -> None:\n",
    "    #TODO: Complete the function according to the instructions, choose simple ansatz.\n",
    "\n",
    "\n",
    "@cfunc\n",
    "def cmain() -> None:\n",
    "    res = vqe(\n",
    "        HAMILTONIAN,\n",
    "        False,\n",
    "        [],\n",
    "        optimizer=Optimizer.COBYLA,\n",
    "        max_iteration=1000,\n",
    "        tolerance=0.001,\n",
    "        step_size=0,\n",
    "        skip_compute_variance=False,\n",
    "        alpha_cvar=1.0,\n",
    "    )\n",
    "    save({\"result\": res})\n",
    "\n",
    "qmod = create_model(main, classical_execution_function=cmain)\n",
    "qprog = synthesize(qmod)\n",
    "show(qprog)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3ef1dab-4ee6-4f96-8e85-d5a8bbd091aa",
   "metadata": {},
   "source": [
    "## H₂ Molecule Problem"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8ad3059-7e43-4ae9-9b80-4c8c5da916ae",
   "metadata": {},
   "source": [
    "Now we will dive into more applicative approche of using VQE with Classiq for modeling molcoules and finding thier ground state and energy.\n",
    "\n",
    "**Hamiltonian for $H_2$ Molecule**\n",
    "\n",
    "The physical Hamiltonian for the hydrogen molecule $H_2$ in the [Born-Oppenheimer approximation](https://en.wikipedia.org/wiki/Born%E2%80%93Oppenheimer_approximation) can be expressed as (atomic units):\n",
    "\n",
    "$$\n",
    "\\hat{H} = - \\sum_{i=1}^{2} \\frac{\\nabla_i^2}{2} - \\frac{1}{|\\mathbf{r}_1 - \\mathbf{R}_A|} - \\frac{1}{|\\mathbf{r}_2 - \\mathbf{R}_A|} - \\frac{1}{|\\mathbf{r}_1 - \\mathbf{R}_B|} - \\frac{1}{|\\mathbf{r}_2 - \\mathbf{R}_B|} + \\frac{1}{|\\mathbf{r}_1 - \\mathbf{r}_2|} + \\frac{1}{|\\mathbf{R}_A - \\mathbf{R}_B|}\n",
    "$$\n",
    "\n",
    "Where:\n",
    "- $\\mathbf{r}_1$ and $\\mathbf{r}_2$ are the positions of the two electrons.\n",
    "- $\\mathbf{R}_A$ and $\\mathbf{R}_B$ are the positions of the two protons (nuclei).\n",
    "- The terms represent:\n",
    "  - The kinetic energy of the electrons: $- \\sum_{i=1}^{2} \\frac{\\nabla_i^2}{2}$\n",
    "  - The electron-nucleus attractions: $- \\frac{1}{|\\mathbf{r}_1 - \\mathbf{R}_A|} - \\frac{1}{|\\mathbf{r}_2 - \\mathbf{R}_A|} - \\frac{1}{|\\mathbf{r}_1 - \\mathbf{R}_B|} - \\frac{1}{|\\mathbf{r}_2 - \\mathbf{R}_B|}$\n",
    "  - The electron-electron repulsion: $\\frac{1}{|\\mathbf{r}_1 - \\mathbf{r}_2|}$\n",
    "  - The nucleus-nucleus repulsion: $\\frac{1}{|\\mathbf{R}_A - \\mathbf{R}_B|}$\n",
    " \n",
    "**How can we implement such a Hamiltonian on a quantum computer?**\n",
    "\n",
    "In the first step of this VQE process, we will need to use:\n",
    "- **Jordan-Wigner Transformation**: To map the fermionic Hamiltonian to a qubit Hamiltonian.\n",
    "- **Hartree-Fock Method**: To provide an initial guess for the wavefunction.\n",
    "- **UCC Ansatz**: To construct the parameterized quantum circuit that will be optimized during the VQE process.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ec42e74-c057-4aff-a3f4-b8f41c97a0f1",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "### Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "e8002d74-1dbf-4e9a-9e3c-0fd0a8d3d710",
   "metadata": {},
   "outputs": [],
   "source": [
    "from classiq import *\n",
    "# from classiq import qfunc, create_model, molecule_ucc, synthesize, show, QBit, allocate, cfunc, execute, QConstant\n",
    "from classiq.qmod.builtins import MoleculeProblem, Molecule, ChemistryAtom, Position\n",
    "from classiq.qmod.builtins.classical_functions import molecule_problem_to_hamiltonian\n",
    "from classiq.qmod.builtins.classical_execution_primitives import vqe, molecule_ground_state_solution_post_process, save\n",
    "from classiq.interface.generator.expressions.enums.chemistry import Element, FermionMapping \n",
    "from classiq.interface.generator.expressions.enums import Optimizer "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f966ab33-bfdd-4f2e-9e63-5cb5751e1797",
   "metadata": {},
   "source": [
    "### implementation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6cbd8ef-19d3-40d6-97c0-8b2c22f226e3",
   "metadata": {},
   "source": [
    "- We will define the `Molecule` structure, as well as the `MoleculeProblem` we are trying to solve.\n",
    "\n",
    "- **The built-in functions we will use here will generate the Hamiltonian and the parameterized circuit for us.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "87f817b3-f03c-47ee-94c5-4ea0feaf6bfb",
   "metadata": {},
   "outputs": [],
   "source": [
    "molecule_H2 = Molecule(\n",
    "    spin=1,\n",
    "    charge=0,\n",
    "    atoms=[\n",
    "        ChemistryAtom(element=Element.H, position=Position(x=0.0, y=0.0, z=0)),\n",
    "        ChemistryAtom(element=Element.H, position=Position(x=0.0, y=0.0, z=0.735)), # Angstrom\n",
    "    ]\n",
    ")\n",
    "\n",
    "\n",
    "chemistry_problem = MoleculeProblem(\n",
    "    molecule=molecule_H2,\n",
    "    mapping=FermionMapping.JORDAN_WIGNER,  #'BRAVYI_KITAEV'\n",
    "    z2_symmetries=False, # If `z2_symmetries=False`, 4 qubits need to be allocated in main\n",
    "    freeze_core=True,\n",
    "    remove_orbitals=[]\n",
    ")\n",
    "\n",
    "molecule_problem = QConstant(\"molecule_problem\", MoleculeProblem, chemistry_problem)\n",
    "\n",
    "@qfunc\n",
    "def main():\n",
    "    q = QArray(\"q\")\n",
    "    allocate(4,q) # allocate 4 qubits if `z2_symmetries=False`, else: allocate 1 qubit\n",
    "    molecule_hartree_fock(molecule_problem,q)\n",
    "    molecule_ucc(molecule_problem = molecule_problem,excitations=[1,2],qbv=q)\n",
    "\n",
    "\n",
    "@cfunc\n",
    "def cmain():\n",
    "    ham = molecule_problem_to_hamiltonian(molecule_problem)\n",
    "    vqe_result = vqe(\n",
    "          hamiltonian=(ham),\n",
    "          maximize=False,\n",
    "          initial_point=[],\n",
    "          optimizer=Optimizer.COBYLA, \n",
    "          max_iteration=100,\n",
    "          tolerance=0.001,\n",
    "          step_size=0,\n",
    "          skip_compute_variance=False,\n",
    "          alpha_cvar=1.0)\n",
    "    molecule_result = molecule_ground_state_solution_post_process(molecule_problem,vqe_result)\n",
    "    save({\"vqe_res\": vqe_result, \"ham\": ham, 'molecule_result': molecule_result}) \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "25c49b37-cdab-4382-9bd8-1f2ceefe2b72",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/20721c4e-d449-4ee1-8aa4-7497ec8b20a5?version=0.41.1\n"
     ]
    }
   ],
   "source": [
    "qmod = create_model(main,classical_execution_function=cmain)\n",
    "qprog = synthesize(qmod)\n",
    "show(qprog)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "07067cd2-caec-4a83-9abb-06b12695d97f",
   "metadata": {},
   "outputs": [],
   "source": [
    "execution = execute(qprog)\n",
    "res = execution.result()\n",
    "execution.open_in_ide()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "3eac8218-ef81-4255-af2c-e2e9666c3197",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "list"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(res)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "297dc43d-d95e-439e-8ec1-0eadf0bfda8c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(res)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "37051ac4-817d-4f0e-a112-c31618b4878d",
   "metadata": {},
   "outputs": [],
   "source": [
    "vqe_result = res[0].value  \n",
    "hamiltonian = res[1].value\n",
    "total_energy = res[2].value['total_energy'] # nuclear_repulsion_energy + vqe_energy\n",
    "nuclear_energy = res[2].value['nuclear_repulsion_energy']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "9efe5a16-4e9e-417e-80ab-7d88d8bf25c5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimal energy: -1.857821819076969 Ha\n",
      "Nuclear repulsion energy: 0.7199689944489797 Ha\n",
      "Optimal parameters: {'param_0': -3.1874228460829723, 'param_1': 0.013694712771728136, 'param_2': 0.11708980402134109}\n",
      "Eigenstate: {'0110': (0.038273277230987154+0j), '1010': (0.08838834764831845+0j), '0101': (0.9953505192895616+0j)}\n"
     ]
    }
   ],
   "source": [
    "print(f\"Optimal energy: {vqe_result.energy} Ha\") # vqe_energy WITHOUT nuclear_repulsion_energy\n",
    "print(f\"Nuclear repulsion energy: {nuclear_energy} Ha\")\n",
    "print(f\"Optimal parameters: {vqe_result.optimal_parameters}\")\n",
    "print(f\"Eigenstate: {vqe_result.eigenstate}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "980c8ab0-88d4-43cf-84b9-7614b4087771",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'pauli': [0, 0, 0, 0], 'coefficient': -0.8105479805373275},\n",
       " {'pauli': [0, 0, 0, 3], 'coefficient': 0.17218393261915557},\n",
       " {'pauli': [0, 0, 3, 0], 'coefficient': -0.2257534922240239},\n",
       " {'pauli': [0, 3, 0, 0], 'coefficient': 0.17218393261915554},\n",
       " {'pauli': [3, 0, 0, 0], 'coefficient': -0.22575349222402397},\n",
       " {'pauli': [0, 0, 3, 3], 'coefficient': 0.12091263261776627},\n",
       " {'pauli': [0, 3, 0, 3], 'coefficient': 0.16892753870087907},\n",
       " {'pauli': [2, 2, 2, 2], 'coefficient': 0.04523279994605784},\n",
       " {'pauli': [1, 1, 2, 2], 'coefficient': 0.04523279994605784},\n",
       " {'pauli': [2, 2, 1, 1], 'coefficient': 0.04523279994605784},\n",
       " {'pauli': [1, 1, 1, 1], 'coefficient': 0.04523279994605784},\n",
       " {'pauli': [3, 0, 0, 3], 'coefficient': 0.16614543256382414},\n",
       " {'pauli': [0, 3, 3, 0], 'coefficient': 0.16614543256382414},\n",
       " {'pauli': [3, 0, 3, 0], 'coefficient': 0.17464343068300445},\n",
       " {'pauli': [3, 3, 0, 0], 'coefficient': 0.12091263261776627}]"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hamiltonian # pauli:[0,1,2,3] == [I,X,Y,Z]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d868399-b5af-46ca-b2da-c0664ae1a827",
   "metadata": {},
   "source": [
    "**Finally, the total energy of the hydrogen molecule is estimated as:**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "a8b7eba2-3be6-453f-a8f4-bfd3feba4d62",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The total energy is: -1.1378528246279895 Ha\n"
     ]
    }
   ],
   "source": [
    "print(f\"The total energy is: {total_energy} Ha\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "e1a8f4ef-d837-4c57-9992-a907677b4ae4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "total_energy == vqe_result.energy + nuclear_energy"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d3d663a-2484-49ba-9400-852d8de141b7",
   "metadata": {},
   "source": [
    "what is the value by theory?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3f881af-3293-454c-aee0-04d1f300c844",
   "metadata": {},
   "source": [
    "**In the third homework assignment, you will compare and validate these results by using 2 qubits Hamiltonian!**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63ad2936-5d73-4bd4-8f91-c62e4cc20f40",
   "metadata": {},
   "source": [
    "## Read More"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce12c5cd-6382-451a-a547-2f564bac546d",
   "metadata": {},
   "source": [
    "\n",
    "- [**VQE Method for Molecule Energy Solver**](https://docs.classiq.io/latest/explore/built_in_apps/chemistry/chemistry/#2-constructing-and-synthesizing-a-ground-state-solver)\n",
    "  - This link provides a detailed demonstration of how to use the `construct_chemistry_model` function, which constructs a VQE model for Molecule eigensolver.\n",
    "\n",
    "- [**Combinatorial Optimization using QAOA**](https://docs.classiq.io/latest/explore/applications/optimization/electric_grid_optimization/electric_grid_optimization/)\n",
    "  - This link covers the use of the Quantum Approximate Optimization Algorithm (QAOA) for combinatorial optimization problems, with a specific example on Electric Grid Optimization.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0213aef6-bd41-4c0a-97e6-c5df3dfb13ab",
   "metadata": {},
   "source": [
    "## Solution - Two Qubits VQE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e12cba4-2e15-46a9-8d2f-852eaa5e135d",
   "metadata": {},
   "outputs": [],
   "source": [
    "HAMILTONIAN = QConstant(\"HAMILTONIAN\", List[PauliTerm], \n",
    "                        [            \n",
    "            PauliTerm([Pauli.I, Pauli.I], 0.5),\n",
    "            PauliTerm([Pauli.Z, Pauli.Z], 0.5),\n",
    "            PauliTerm([Pauli.X, Pauli.X], -1)\n",
    "                        ])\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(q: Output[QArray[QBit]], angles: CArray[CReal, 3]) -> None:\n",
    "    allocate(2, q)\n",
    "    U(angles[0], angles[1], angles[2], 0, q[0])\n",
    "    U(angles[0], angles[1], angles[2], 0, q[1])\n",
    "\n",
    "\n",
    "@cfunc\n",
    "def cmain() -> None:\n",
    "    res = vqe(\n",
    "        HAMILTONIAN,\n",
    "        False,\n",
    "        [],\n",
    "        optimizer=Optimizer.COBYLA,\n",
    "        max_iteration=1000,\n",
    "        tolerance=0.001,\n",
    "        step_size=0,\n",
    "        skip_compute_variance=False,\n",
    "        alpha_cvar=1.0,\n",
    "    )\n",
    "    save({\"result\": res})\n",
    "\n",
    "qmod = create_model(main, classical_execution_function=cmain)\n",
    "qprog = synthesize(qmod)\n",
    "# show(qprog)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
